﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Diagnostics;
using LightingServer.Tool;
using System.Threading;
using System.IO;

namespace LightingServer.TCP
{

    public class ClientState
    {
        public NetworkStream Stream { get; set; }
        public byte[] BufferCache { get; set; }
        public byte[] ReceiveData { get; set; }
    }

    public class TCPServer
    {
        private TcpListener tcpListener;
        private Thread listenThread;

        public void Start()
        {
            this.tcpListener = new TcpListener(IPAddress.Any, Registrar.TCPListenPort);
            this.listenThread = new Thread(new ThreadStart(ListenForClients));
            this.listenThread.Start();
        }

        private void ListenForClients()
        {
            this.tcpListener.Start();

            while (true)
            {
                //blocks until a client has connected to the server
                TcpClient client = this.tcpListener.AcceptTcpClient();

                //create a thread to handle communication 
                //with connected client
                Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
                clientThread.Start(client);
            }
        }

        private void HandleClientComm(object client)
        {
            TcpClient tcpClient = (TcpClient)client;

            MemoryStream stream = null;
            while (true)
            {
                try
                {
                    NetworkStream networkStream = tcpClient.GetStream();

                    byte[] buffer = new byte[tcpClient.ReceiveBufferSize];
                    int length = networkStream.Read(buffer, 0, tcpClient.ReceiveBufferSize);

                    for (int i = 0; i < length; i++)
                    {
                        byte value = buffer[i];

                        switch (value)
                        {
                            case ProtocolData.Head:
                                stream = new MemoryStream();
                                break;
                            case ProtocolData.Tail:
                                if (stream != null)
                                {
                                    stream.Position = 0;
                                    Decode(stream, networkStream);
                                }
                                stream = null;
                                break;
                            default:
                                if (stream != null)
                                {
                                    stream.WriteByte(buffer[i]);
                                }
                                break;
                        }
                    }

                }
                catch (Exception ex)
                {
                    if (tcpClient != null)
                    {
                        try
                        {
                            tcpClient.Client.Shutdown(SocketShutdown.Both);
                            tcpClient.Client.Close();
                        }
                        catch
                        {
                        }
                    }
                    LogTool.Log(ex.ToString());
                    break;
                }
            }
        }

        public void Decode(MemoryStream stream, Stream tcpStream)
        {
            ProtocolData protocol = new ProtocolData();
            using (stream)
            {
                byte header0 = this.Read(stream);
                byte header1 = this.Read(stream);
                protocol.CommandID = (ushort)((header0 << 8) + header1);

                byte header2 = this.Read(stream);
                byte header3 = this.Read(stream);
                byte header4 = this.Read(stream);
                byte header5 = this.Read(stream);
                protocol.SerialNumber = (header2 << 24) + (header3 << 16) + (header4 << 8) + (header5 << 0);

                byte header6 = this.Read(stream);
                byte header7 = this.Read(stream);
                byte header8 = this.Read(stream);
                byte header9 = this.Read(stream);
                protocol.Length = (header6 << 24) + (header7 << 16) + (header8 << 8) + header9;

                List<byte> list = new List<byte>();
                while (stream.Position != stream.Length)
                {
                    list.Add(this.Read(stream));
                }

                if (protocol.Length == list.Count)
                {
                    protocol.Data = list.ToArray();
                    LogTool.Log(" >> 接收的数据长度" + protocol.Data.Length);
                }
            }

            LogTool.Log(" >> 处理请求开始......");
            byte[] response = null;
            try
            {
                IServerCommand command = Registrar.GetCommand(protocol.CommandID);
                response = command.Excute(protocol.Data);
            }
            catch (Exception ex)
            {
                response = new byte[] { 0 };
            }

            ProtocolEncoder encoder = new ProtocolEncoder();
            ProtocolData sendProtocol = new ProtocolData()
            {
                CommandID = protocol.CommandID,
                SerialNumber = protocol.SerialNumber,
                Length = response.Length,
                Data = response
            };

            byte[] sendBytes = encoder.Encode(sendProtocol);
            tcpStream.Write(sendBytes, 0, sendBytes.Length);
            tcpStream.Flush();

            LogTool.Log(" >> 发送的数据长度：" + sendBytes.Length);
        }

        private byte Read(MemoryStream stream)
        {
            byte value = (byte)stream.ReadByte();
            switch (value)
            {
                case ProtocolData.HeadEscape:
                    switch ((byte)stream.ReadByte())
                    {
                        case ProtocolData.HeadEscape1:
                            return ProtocolData.Head;
                        case ProtocolData.HeadEscape2:
                            return ProtocolData.HeadEscape;
                        default:
                            throw new FormatException();
                    }
                case ProtocolData.TailEscape:
                    switch ((byte)stream.ReadByte())
                    {
                        case ProtocolData.TailEscape1:
                            return ProtocolData.Tail;
                        case ProtocolData.TailEscape2:
                            return ProtocolData.TailEscape;
                        default:
                            throw new FormatException();
                    }
            }

            return value;
        }
    }
}
